/** ****************************************************************************
* @defgroup BSP BSP(Board Support Package)
* @brief BSP(Board Support Package)层定义
* @addtogroup BSP
* @{
*******************************************************************************/

#include "stdinc.h"


void GPIN_Config(GPIO_TypeDef* port, uint8_t pin, uint8_t cfg) {
	(cfg & BV(2)) ? (port->DDR |= BV(pin)) : (port->DDR &= ~BV(pin));
	(cfg & BV(1)) ? (port->CR1 |= BV(pin)) : (port->CR1 &= ~BV(pin));
	(cfg & BV(0)) ? (port->CR2 |= BV(pin)) : (port->CR2 &= ~BV(pin));
}

uint8_t isValidEntry(uint16_t entry) {
	uint16_t opCode 		= *((uint16_t*)entry);
    uint16_t entryFunc 	= *((uint16_t*)entry+2);
    return (opCode == 0x8200 ) &&
           ISBETWEEN(entryFunc, FLASH_PROGRAM_START_PHYSICAL_ADDRESS, FLASH_PROGRAM_END_PHYSICAL_ADDRESS);
}

// 要跳转到的目标程序中断向量表地址
// ！！！注意：必须使用全局变量，局部变量在汇编中无法访问！！！
static uint16_t _dstIntvecAddress;

void jump2Entry(uint16_t entry) {
    if( !isValidEntry(entry) ) {
		printf("Can't jump to invalid entry: 0x%04X\r\n", entry);
        return;
    }
    printf("Jump to Entry at 0x%04lX ...\r\n", entry);

    // 初始化程序入口地址
	_dstIntvecAddress = entry;
    // 必须先关闭全局中断
    disableInterrupts();

    // 清空堆栈，把SP恢复到程序启动状态
    ASM("LDW X,  SP");
    ASM("LD  A,  $FF");
    ASM("LD  XL, A");
    ASM("LDW SP, X");

    // 跳转到 Flash 中 Bootloader 所在位置
    // 不能使用 JPF 指令，因为它无法用变量指定跳转地址
    ASM("LDW X, _dstIntvecAddress");
    ASM("JP(X)");
}

uint8_t isInDebugging() {
	return (RST_GetFlagStatus(RST_FLAG_SWIMF) == SET);
}

uint8_t isAbnormalReboot() {
	uint8_t abnormalReboot = false;
	uint8_t rst = RST->SR;
	printf("RST_FLAG = 0x%02X\r\n", rst);

	if(rst & RST_FLAG_PORF) {
		printf("* Power-on reset (POR)\r\n");
	}
	if(rst & RST_FLAG_BORF) {
		printf("* Brownout reset (BOR)\r\n");
	}
	if(rst & RST_FLAG_SWIMF) {
		printf("* SWIM reset\r\n");
	}

	if(rst & RST_FLAG_ILLOPF) {
		printf("* Illegal opcode reset\r\n");
		abnormalReboot = true;
		RST_ClearFlag(RST_FLAG_ILLOPF);
	}
	if(rst & RST_FLAG_IWDGF) {
		printf("* Independent watchdog reset\r\n");
		abnormalReboot = true;
		RST_ClearFlag(RST_FLAG_IWDGF);
	}
	if(rst & RST_FLAG_WWDGF) {
		printf("* Window watchdog reset\r\n");
		abnormalReboot = true;
		RST_ClearFlag(RST_FLAG_WWDGF);
	}

	return abnormalReboot;
}

// 看门狗初始化
/*
 Prescaler divider      PR[2:0] bits                 Timeout (ms)
											 RL[7:0]= 0x00   RL[7:0]= 0xFF
		/4                   0                    0.11            26.95
		/8                   1                    0.21            53.89
		/16                  2                    0.42            107.79
		/32                  3                    0.84            215.58
		/64                  4                    1.68            431.16
		/128                 5                    3.37            862.32
		/256                 6                    6.74            1724.63
 */
void Watchdog_init(void) {
	IWDG_Enable();
	IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); //进入写保护
	IWDG_SetPrescaler(IWDG_Prescaler_256);
	IWDG_SetReload(0xff);
	IWDG_ReloadCounter();
}


// 电量百分比<->电压阀值对照表
/*
  PWR_PVDLevel_1V85  = (uint8_t)0x00, /// PVD threshold = 1.85 V
  PWR_PVDLevel_2V05  = (uint8_t)0x02, /// PVD threshold = 2.05 V
  PWR_PVDLevel_2V26  = (uint8_t)0x04, /// PVD threshold = 2.26 V
  PWR_PVDLevel_2V45  = (uint8_t)0x06, /// PVD threshold = 2.45 V
  PWR_PVDLevel_2V65  = (uint8_t)0x08, /// PVD threshold = 2.65 V
  PWR_PVDLevel_2V85  = (uint8_t)0x0A, /// PVD threshold = 2.85 V
  PWR_PVDLevel_3V05  = (uint8_t)0x0C, /// PVD threshold = 3.05 V
*/
#define PWR_PVD_LEVEL_COUNT 7
const static uint8_t PowerLevelMap[PWR_PVD_LEVEL_COUNT][3] = {
	{PWR_PVDLevel_3V05, 100, 31},
	{PWR_PVDLevel_2V85,  80, 29},
	{PWR_PVDLevel_2V65,  60, 27},
	{PWR_PVDLevel_2V45,  40, 25},
	{PWR_PVDLevel_2V26,  30, 23},
	{PWR_PVDLevel_2V05,  20, 21},
	{PWR_PVDLevel_1V85,  10, 19},
};

// 当前低电量告警阀值索引
static uint8_t _pvdi = 0;

// 获取当前电量百分比
uint8_t PWR_PVD_getPercent() {
	return (_pvdi >= PWR_PVD_LEVEL_COUNT) ? 0 : PowerLevelMap[_pvdi][1];
}

// 获取当前电压(单位0.1mV)
uint8_t PWR_PVD_getVolt() {
	return (_pvdi >= PWR_PVD_LEVEL_COUNT) ? 18 : PowerLevelMap[_pvdi][2];
}

// 低电量告警处理回调函数
// @param percent 当前电量百分比
// @param volt 当前电电压
extern void APP_onLowPower(uint8_t percent, uint8_t volt);

// 电量检查
void PWR_PVD_check(uint8_t len, void* data) {
	// 事件重复次数
	static uint8_t _pvdc = 0;

	if(PWR_GetFlagStatus(PWR_FLAG_PVDOF) == RESET) {
		// PVDOF 高于门限时为低电平，此时应提高门限
		if(_pvdi > 0) { _pvdi--; _pvdc=0; }
	} else {
		// 清除标志
		PWR_PVDClearFlag();
		// PVDOF 低于门限时为高电平，此时应降低门限
		if(_pvdi < PWR_PVD_LEVEL_COUNT) { _pvdi++; _pvdc=0; }
		// 如果电压过低，则报警处理
		if(PWR_PVD_getPercent() <= 30) {
			// 低电量事件计数，达到3次第一次报警，以后每60次报警一次
			_pvdc = (_pvdc+1)%60;
			if(_pvdc == 3) {
				// 调用低电量告警处理函数
				APP_onLowPower(PWR_PVD_getPercent(), PWR_PVD_getVolt());
			}
		}
	}

	// 重新设置门限
	PWR_PVDLevelConfig((PWR_PVDLevel_TypeDef)PowerLevelMap[_pvdi][0]);
}

// 低电量检测初始化
void PWR_PVD_Init() {
  	PWR_PVDLevelConfig(PWR_PVDLevel_3V05);	// PVD门限设置
	PWR_PVDClearFlag();						// 清除标志
  	PWR_PVDCmd(ENABLE);  					// PVD使能
  	PWR_PVDITConfig(DISABLE);    			// PVD中断禁用，使用定时轮询模式

	// 1秒钟检查一次电量
	OSTaskRunPeriodically(1000, PWR_PVD_check, 0, NULL);
}


/// @}
